summaryrefslogtreecommitdiff
path: root/app/[lng]/evcp/(evcp)/(procurement)/bid
diff options
context:
space:
mode:
Diffstat (limited to 'app/[lng]/evcp/(evcp)/(procurement)/bid')
-rw-r--r--app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/bidding-tabs.tsx77
-rw-r--r--app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/companies/page.tsx75
-rw-r--r--app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/detail/page.tsx51
-rw-r--r--app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/info/page.tsx75
-rw-r--r--app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/items/page.tsx76
-rw-r--r--app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/layout.tsx92
-rw-r--r--app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/page.tsx12
-rw-r--r--app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/pre-quote/page.tsx56
-rw-r--r--app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/schedule/page.tsx73
-rw-r--r--app/[lng]/evcp/(evcp)/(procurement)/bid/page.tsx78
10 files changed, 380 insertions, 285 deletions
diff --git a/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/bidding-tabs.tsx b/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/bidding-tabs.tsx
new file mode 100644
index 00000000..0321d273
--- /dev/null
+++ b/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/bidding-tabs.tsx
@@ -0,0 +1,77 @@
+"use client"
+
+import * as React from "react"
+import { usePathname, useRouter } from "next/navigation"
+import { Button } from "@/components/ui/button"
+import { cn } from "@/lib/utils"
+
+interface BiddingTabsProps {
+ id: string
+}
+
+export function BiddingTabs({ id }: BiddingTabsProps) {
+ const pathname = usePathname()
+ const router = useRouter()
+
+ const tabs = React.useMemo(() => [
+ {
+ key: "info",
+ label: "입찰 기본 정보",
+ href: `/evcp/bid/${id}/info`,
+ },
+ {
+ key: "companies",
+ label: "입찰 업체",
+ href: `/evcp/bid/${id}/companies`,
+ },
+ {
+ key: "items",
+ label: "입찰 품목",
+ href: `/evcp/bid/${id}/items`,
+ },
+ {
+ key: "schedule",
+ label: "입찰 계획",
+ href: `/evcp/bid/${id}/schedule`,
+ },
+ ], [id])
+
+ // 현재 활성 탭 결정
+ const activeTab = React.useMemo(() => {
+ if (!pathname) return "info"
+
+ // pathname에서 lng 부분 제거 (예: /en/evcp/bid/10 -> /evcp/bid/10)
+ const normalizedPath = pathname.replace(/^\/[^/]+/, '') || pathname
+
+ // 기본 페이지는 info로 처리
+ if (normalizedPath === `/evcp/bid/${id}` || normalizedPath.endsWith(`/bid/${id}`)) {
+ return "info"
+ }
+
+ const matchedTab = tabs.find(tab => normalizedPath.includes(`/${tab.key}`))
+ return matchedTab?.key || "info"
+ }, [pathname, id, tabs])
+
+ return (
+ <div className="flex items-center gap-1">
+ {tabs.map((tab) => {
+ const isActive = activeTab === tab.key
+ return (
+ <Button
+ key={tab.key}
+ variant={isActive ? "secondary" : "ghost"}
+ size="default"
+ className={cn(
+ "text-md px-3 py-1 h-7",
+ isActive && "bg-secondary"
+ )}
+ onClick={() => router.push(tab.href)}
+ >
+ {tab.label}
+ </Button>
+ )
+ })}
+ </div>
+ )
+}
+
diff --git a/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/companies/page.tsx b/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/companies/page.tsx
new file mode 100644
index 00000000..f1699665
--- /dev/null
+++ b/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/companies/page.tsx
@@ -0,0 +1,75 @@
+import { notFound } from 'next/navigation'
+import { getBiddingById } from "@/lib/bidding/service"
+import { Bidding } from "@/db/schema/bidding"
+import { Button } from "@/components/ui/button"
+import { ArrowLeft } from "lucide-react"
+import Link from "next/link"
+import { BiddingCompaniesEditor } from "@/components/bidding/manage/bidding-companies-editor"
+import { BiddingTabs } from "../bidding-tabs"
+
+// 메타데이터 생성
+export async function generateMetadata({ params }: { params: Promise<{ lng: string; id: string }> }) {
+ const { lng, id } = await params
+ const parsedId = parseInt(id)
+ if (isNaN(parsedId)) return { title: '입찰 업체 및 담당자 관리' }
+
+ try {
+ const bidding = await getBiddingById(parsedId)
+ return {
+ title: bidding ? `${bidding.title} - 입찰 업체 및 담당자 관리` : '입찰 업체 및 담당자 관리',
+ }
+ } catch {
+ return { title: '입찰 업체 및 담당자 관리' }
+ }
+}
+
+interface PageProps {
+ params: Promise<{ lng: string; id: string }>
+}
+
+export default async function BiddingCompaniesPage({ params }: PageProps) {
+ const { lng, id } = await params
+ const parsedId = parseInt(id)
+
+ if (isNaN(parsedId)) {
+ notFound()
+ }
+
+ const bidding: Bidding | null = await getBiddingById(parsedId)
+
+ if (!bidding) {
+ notFound()
+ }
+
+ return (
+ <div className="container py-6 space-y-6">
+ {/* 헤더 */}
+ <div className="flex justify-between items-center">
+ <div className="flex items-center gap-4">
+ <div>
+ <h1 className="text-3xl font-bold tracking-tight">
+ 입찰 업체 및 담당자 관리
+ </h1>
+ <p className="text-muted-foreground mt-2">
+ 입찰 No. {bidding.biddingNumber ?? ""} - {bidding.title}
+ </p>
+ </div>
+ </div>
+ <Link href={`/${lng}/evcp/bid/${id}`} passHref>
+ <Button variant="outline" className="flex items-center">
+ <ArrowLeft className="mr-2 h-4 w-4" />
+ 입찰 관리로 돌아가기
+ </Button>
+ </Link>
+ </div>
+
+ {/* 탭 네비게이션 */}
+ <div>
+ <BiddingTabs id={id} />
+ </div>
+
+ {/* 입찰 업체 및 담당자 에디터 */}
+ <BiddingCompaniesEditor biddingId={parsedId} />
+ </div>
+ )
+}
diff --git a/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/detail/page.tsx b/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/detail/page.tsx
deleted file mode 100644
index 4dc36e20..00000000
--- a/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/detail/page.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-import { Suspense } from 'react'
-import { notFound } from 'next/navigation'
-import { getBiddingDetailData } from '@/lib/bidding/detail/service'
-import { BiddingDetailContent } from '@/lib/bidding/detail/table/bidding-detail-content'
-
-// 메타데이터 생성
-export async function generateMetadata({ params }: { params: Promise<{ id: string }> }) {
- const { id } = await params
- const parsedId = parseInt(id)
- if (isNaN(parsedId)) return { title: '입찰 관리상세' }
-
- try {
- const detailData = await getBiddingDetailData(parsedId)
- return {
- title: detailData.bidding ? `${detailData.bidding.title} - 입찰 관리상세` : '입찰 관리상세',
- }
- } catch {
- return { title: '입찰 관리상세' }
- }
-}
-
-interface PageProps {
- params: Promise<{ id: string }>
-}
-
-export default async function Page({ params }: PageProps) {
- const { id } = await params
- const parsedId = parseInt(id)
-
- if (isNaN(parsedId)) {
- notFound()
- }
-
- // 통합 데이터 로딩 함수 사용
- const detailData = await getBiddingDetailData(parsedId)
-
- if (!detailData.bidding) {
- notFound()
- }
-
- return (
- <Suspense fallback={<div className="p-8">로딩 중...</div>}>
- <BiddingDetailContent
- bidding={detailData.bidding}
- quotationDetails={detailData.quotationDetails}
- quotationVendors={detailData.quotationVendors}
- prItems={detailData.prItems}
- />
- </Suspense>
- )
-}
diff --git a/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/info/page.tsx b/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/info/page.tsx
new file mode 100644
index 00000000..7281d206
--- /dev/null
+++ b/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/info/page.tsx
@@ -0,0 +1,75 @@
+import { notFound } from 'next/navigation'
+import { getBiddingById } from "@/lib/bidding/service"
+import { Bidding } from "@/db/schema/bidding"
+import { Button } from "@/components/ui/button"
+import { ArrowLeft } from "lucide-react"
+import Link from "next/link"
+import { BiddingBasicInfoEditor } from "@/components/bidding/manage/bidding-basic-info-editor"
+import { BiddingTabs } from "../bidding-tabs"
+
+// 메타데이터 생성
+export async function generateMetadata({ params }: { params: Promise<{ lng: string; id: string }> }) {
+ const { id } = await params
+ const parsedId = parseInt(id)
+ if (isNaN(parsedId)) return { title: '입찰 기본 정보 관리' }
+
+ try {
+ const bidding = await getBiddingById(parsedId)
+ return {
+ title: bidding ? `${bidding.title} - 입찰 기본 정보 관리` : '입찰 기본 정보 관리',
+ }
+ } catch {
+ return { title: '입찰 기본 정보 관리' }
+ }
+}
+
+interface PageProps {
+ params: Promise<{ lng: string; id: string }>
+}
+
+export default async function BiddingBasicInfoPage({ params }: PageProps) {
+ const { lng, id } = await params
+ const parsedId = parseInt(id)
+
+ if (isNaN(parsedId)) {
+ notFound()
+ }
+
+ const bidding: Bidding | null = await getBiddingById(parsedId)
+
+ if (!bidding) {
+ notFound()
+ }
+
+ return (
+ <div className="container py-6 space-y-6">
+ {/* 헤더 */}
+ <div className="flex justify-between items-center">
+ <div className="flex items-center gap-4">
+ <div>
+ <h1 className="text-3xl font-bold tracking-tight">
+ 입찰 기본 정보 관리
+ </h1>
+ <p className="text-muted-foreground mt-2">
+ 입찰 No. {bidding.biddingNumber ?? ""} - {bidding.title}
+ </p>
+ </div>
+ </div>
+ <Link href={`/${lng}/evcp/bid/${id}`} passHref>
+ <Button variant="outline" className="flex items-center">
+ <ArrowLeft className="mr-2 h-4 w-4" />
+ 입찰 관리로 돌아가기
+ </Button>
+ </Link>
+ </div>
+
+ {/* 탭 네비게이션 */}
+ <div>
+ <BiddingTabs id={id} />
+ </div>
+
+ {/* 입찰 기본 정보 에디터 */}
+ <BiddingBasicInfoEditor biddingId={parsedId} />
+ </div>
+ )
+}
diff --git a/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/items/page.tsx b/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/items/page.tsx
new file mode 100644
index 00000000..5b686a1c
--- /dev/null
+++ b/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/items/page.tsx
@@ -0,0 +1,76 @@
+import { notFound } from 'next/navigation'
+import { getBiddingById } from "@/lib/bidding/service"
+import { Bidding } from "@/db/schema/bidding"
+import { Button } from "@/components/ui/button"
+import { ArrowLeft } from "lucide-react"
+import Link from "next/link"
+import { BiddingItemsEditor } from "@/components/bidding/manage/bidding-items-editor"
+import { BiddingTabs } from "../bidding-tabs"
+
+// 메타데이터 생성
+export async function generateMetadata({ params }: { params: Promise<{ lng: string; id: string }> }) {
+ const { lng, id } = await params
+ const parsedId = parseInt(id)
+ if (isNaN(parsedId)) return { title: '입찰 품목 관리' }
+
+ try {
+ const bidding = await getBiddingById(parsedId)
+ return {
+ title: bidding ? `${bidding.title} - 입찰 품목 관리` : '입찰 품목 관리',
+ }
+ } catch {
+ return { title: '입찰 품목 관리' }
+ }
+}
+
+interface PageProps {
+ params: Promise<{ lng: string; id: string }>
+}
+
+export default async function BiddingItemsPage({ params }: PageProps) {
+ const { lng, id } = await params
+ const parsedId = parseInt(id)
+
+ if (isNaN(parsedId)) {
+ notFound()
+ }
+
+ const bidding: Bidding | null = await getBiddingById(parsedId)
+
+ if (!bidding) {
+ notFound()
+ }
+
+ return (
+ <div className="container py-6 space-y-6">
+ {/* 헤더 */}
+ <div className="flex justify-between items-center">
+ <div className="flex items-center gap-4">
+ <div>
+ <h1 className="text-3xl font-bold tracking-tight">
+ 입찰 품목 관리
+ </h1>
+ <p className="text-muted-foreground mt-2">
+ 입찰 No. {bidding.biddingNumber ?? ""} - {bidding.title}
+ </p>
+ </div>
+
+ </div>
+ <Link href={`/${lng}/evcp/bid/${id}`} passHref>
+ <Button variant="outline" className="flex items-center">
+ <ArrowLeft className="mr-2 h-4 w-4" />
+ 입찰 관리로 돌아가기
+ </Button>
+ </Link>
+ </div>
+
+ {/* 탭 네비게이션 */}
+ <div>
+ <BiddingTabs id={id} />
+ </div>
+
+ {/* 입찰 품목 에디터 */}
+ <BiddingItemsEditor biddingId={parsedId} />
+ </div>
+ )
+}
diff --git a/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/layout.tsx b/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/layout.tsx
deleted file mode 100644
index 80e7f8d2..00000000
--- a/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/layout.tsx
+++ /dev/null
@@ -1,92 +0,0 @@
-import { Metadata } from "next"
-
-import { Separator } from "@/components/ui/separator"
-import { SidebarNav } from "@/components/layout/sidebar-nav"
-import { getBiddingById, getBiddingConditions } from "@/lib/bidding/service"
-import { Bidding } from "@/db/schema/bidding"
-import { Button } from "@/components/ui/button"
-import { ArrowLeft } from "lucide-react"
-import Link from "next/link"
-import { BiddingInfoHeader } from "@/components/bidding/bidding-info-header"
-import { BiddingConditionsEdit } from "@/components/bidding/bidding-conditions-edit"
-export const metadata: Metadata = {
- title: "Bidding Detail",
-}
-
-export default async function SettingsLayout({
- children,
- params,
-}: {
- children: React.ReactNode
- params: { lng: string , id: string}
-}) {
-
- // 1) URL 파라미터에서 id 추출, Number로 변환
- const resolvedParams = await params
- const lng = resolvedParams.lng
- const id = resolvedParams.id
-
- const idAsNumber = Number(id)
- // 2) DB에서 해당 입찰 정보 조회
- const bidding: Bidding | null = await getBiddingById(idAsNumber)
- const biddingConditions = await getBiddingConditions(idAsNumber)
-
- // 3) 사이드바 메뉴
- const sidebarNavItems = [
- {
- title: "입찰 사전견적",
- href: `/${lng}/evcp/bid/${id}/pre-quote`,
- },
- {
- title: "입찰 관리상세",
- href: `/${lng}/evcp/bid/${id}/detail`,
- },
- ]
-
- return (
- <>
- <div className="container py-6">
- <section className="overflow-hidden rounded-[0.5rem] border bg-background shadow">
- <div className="hidden space-y-6 p-10 pb-16 md:block">
- {/* RFQ 목록으로 돌아가는 링크 추가 */}
- <div className="flex justify-between items-center mb-4">
- <div>
- {/* 4) 입찰 정보가 있으면 번호 + 제목 + "상세 정보" 표기 */}
- <h2 className="text-2xl font-bold tracking-tight">
- {bidding
- ? `입찰 No. ${bidding.biddingNumber ?? ""} - ${bidding.title}`
- : "Loading Bidding..."}
- </h2>
- </div>
- <Link href={`/${lng}/evcp/bid`} passHref>
- <Button variant="ghost" className="flex items-center text-primary hover:text-primary/80 transition-colors p-0 h-auto">
- <ArrowLeft className="mr-1 h-4 w-4" />
- <span>입찰 목록으로 돌아가기</span>
- </Button>
- </Link>
- </div>
-
- {/* 입찰 정보 헤더 */}
- <BiddingInfoHeader bidding={bidding} />
-
- {/* 입찰 조건 */}
- {bidding && (
- <BiddingConditionsEdit
- biddingId={bidding.id}
- initialConditions={biddingConditions}
- />
- )}
-
- <Separator className="my-6" />
- <div className="flex flex-col space-y-8 lg:flex-row lg:space-x-12 lg:space-y-0">
- <aside className="-mx-4 lg:w-1/5">
- <SidebarNav items={sidebarNavItems} />
- </aside>
- <div className="flex-1 overflow-auto max-w-full">{children}</div>
- </div>
- </div>
- </section>
- </div>
- </>
- )
-} \ No newline at end of file
diff --git a/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/page.tsx b/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/page.tsx
deleted file mode 100644
index ca0788a5..00000000
--- a/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/page.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import { redirect } from 'next/navigation'
-
-interface PageProps {
- params: Promise<{ lng: string; id: string }>
-}
-
-export default async function Page({ params }: PageProps) {
- const { lng, id } = await params
-
- // 기본적으로 입찰 사전견적 페이지로 리다이렉트
- redirect(`/${lng}/evcp/bid/${id}/pre-quote`)
-}
diff --git a/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/pre-quote/page.tsx b/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/pre-quote/page.tsx
deleted file mode 100644
index d978974b..00000000
--- a/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/pre-quote/page.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import { Suspense } from 'react'
-import { notFound } from 'next/navigation'
-import { getBiddingDetailData } from '@/lib/bidding/detail/service'
-import { getBiddingCompanies } from '@/lib/bidding/pre-quote/service'
-import { BiddingPreQuoteContent } from '@/lib/bidding/pre-quote/table/bidding-pre-quote-content'
-
-// 메타데이터 생성
-export async function generateMetadata({ params }: { params: Promise<{ id: string }> }) {
- const { id } = await params
- const parsedId = parseInt(id)
- if (isNaN(parsedId)) return { title: '입찰 사전견적' }
-
- try {
- const detailData = await getBiddingDetailData(parsedId)
- return {
- title: detailData.bidding ? `${detailData.bidding.title} - 입찰 사전견적` : '입찰 사전견적',
- }
- } catch {
- return { title: '입찰 사전견적' }
- }
-}
-
-interface PageProps {
- params: Promise<{ id: string }>
-}
-
-export default async function Page({ params }: PageProps) {
- const { id } = await params
- const parsedId = parseInt(id)
-
- if (isNaN(parsedId)) {
- notFound()
- }
-
- // 통합 데이터 로딩 함수 사용
- const detailData = await getBiddingDetailData(parsedId)
-
- if (!detailData.bidding) {
- notFound()
- }
-
- // 사전견적용 입찰 업체들 조회
- const biddingCompaniesResult = await getBiddingCompanies(parsedId)
- const biddingCompanies = biddingCompaniesResult?.success ? biddingCompaniesResult.data || [] : []
-
- return (
- <Suspense fallback={<div className="p-8">로딩 중...</div>}>
- <BiddingPreQuoteContent
- bidding={detailData.bidding}
- quotationDetails={detailData.quotationDetails}
- biddingCompanies={biddingCompanies}
- prItems={detailData.prItems}
- />
- </Suspense>
- )
-}
diff --git a/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/schedule/page.tsx b/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/schedule/page.tsx
new file mode 100644
index 00000000..a79bef88
--- /dev/null
+++ b/app/[lng]/evcp/(evcp)/(procurement)/bid/[id]/schedule/page.tsx
@@ -0,0 +1,73 @@
+import { notFound } from 'next/navigation'
+import { getBiddingById } from "@/lib/bidding/service"
+import { Bidding } from "@/db/schema/bidding"
+import { Button } from "@/components/ui/button"
+import { ArrowLeft } from "lucide-react"
+import Link from "next/link"
+import { BiddingScheduleEditor } from "@/components/bidding/manage/bidding-schedule-editor"
+import { BiddingTabs } from "../bidding-tabs"
+
+// 메타데이터 생성
+export async function generateMetadata({ params }: { params: Promise<{ lng: string; id: string }> }) {
+ const { lng, id } = await params
+ const parsedId = parseInt(id)
+ if (isNaN(parsedId)) return { title: '입찰 일정 관리' }
+
+ try {
+ const bidding = await getBiddingById(parsedId)
+ return {
+ title: bidding ? `${bidding.title} - 입찰 일정 관리` : '입찰 일정 관리',
+ }
+ } catch {
+ return { title: '입찰 일정 관리' }
+ }
+}
+
+interface PageProps {
+ params: Promise<{ lng: string; id: string }>
+}
+
+export default async function BiddingSchedulePage({ params }: PageProps) {
+ const { lng, id } = await params
+ const parsedId = parseInt(id)
+
+ if (isNaN(parsedId)) {
+ notFound()
+ }
+
+ const bidding: Bidding | null = await getBiddingById(parsedId)
+
+ if (!bidding) {
+ notFound()
+ }
+
+ return (
+ <div className="container py-6 space-y-6">
+ {/* 헤더 */}
+ <div className="flex justify-between items-center">
+ <div>
+ <h1 className="text-3xl font-bold tracking-tight">
+ 입찰 일정 관리
+ </h1>
+ <p className="text-muted-foreground mt-2">
+ 입찰 No. {bidding.biddingNumber ?? ""} - {bidding.title}
+ </p>
+ </div>
+ <Link href={`/${lng}/evcp/bid/${id}`} passHref>
+ <Button variant="outline" className="flex items-center">
+ <ArrowLeft className="mr-2 h-4 w-4" />
+ 입찰 관리로 돌아가기
+ </Button>
+ </Link>
+ </div>
+
+ {/* 탭 네비게이션 */}
+ <div>
+ <BiddingTabs id={id} />
+ </div>
+
+ {/* 입찰 일정 에디터 */}
+ <BiddingScheduleEditor biddingId={parsedId} />
+ </div>
+ )
+}
diff --git a/app/[lng]/evcp/(evcp)/(procurement)/bid/page.tsx b/app/[lng]/evcp/(evcp)/(procurement)/bid/page.tsx
index aa9f33b5..973593d8 100644
--- a/app/[lng]/evcp/(evcp)/(procurement)/bid/page.tsx
+++ b/app/[lng]/evcp/(evcp)/(procurement)/bid/page.tsx
@@ -4,14 +4,9 @@ import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
import {
getBiddings,
getBiddingStatusCounts,
- getBiddingTypeCounts,
- getBiddingManagerCounts,
- getBiddingMonthlyStats,
- getUserCodeByEmail,
} from "@/lib/bidding/service"
import { searchParamsCache } from "@/lib/bidding/validation"
import { BiddingsPageHeader } from "@/lib/bidding/list/biddings-page-header"
-import { BiddingsStatsCards } from "@/lib/bidding/list/biddings-stats-cards"
import { BiddingsTable } from "@/lib/bidding/list/biddings-table"
import { getValidFilters } from "@/lib/data-table"
import { type SearchParams } from "@/types/table"
@@ -33,30 +28,13 @@ export default async function BiddingsPage(props: IndexPageProps) {
const validFilters = getValidFilters(search.filters)
- // ✅ 입찰 데이터를 먼저 가져옴
- const biddingsResult = await getBiddings({
- ...search,
- filters: validFilters,
- })
-
- // ✅ 입찰 데이터에 managerCode 추가
- const biddingsDataWithManagerCode = await Promise.all(
- biddingsResult.data.map(async (item) => {
- let managerCode: string | null = null
- if (item.managerEmail) {
- managerCode = await getUserCodeByEmail(item.managerEmail)
- }
- return { ...item, managerCode: managerCode || null }
- })
- )
-
// ✅ 모든 데이터를 병렬로 로드
const promises = Promise.all([
- Promise.resolve({ ...biddingsResult, data: biddingsDataWithManagerCode }),
+ getBiddings({
+ ...search,
+ filters: validFilters,
+ }),
getBiddingStatusCounts(),
- getBiddingTypeCounts(),
- getBiddingManagerCounts(),
- getBiddingMonthlyStats(),
])
return (
@@ -67,13 +45,6 @@ export default async function BiddingsPage(props: IndexPageProps) {
<BiddingsPageHeader />
{/* ═══════════════════════════════════════════════════════════════ */}
- {/* 통계 카드들 */}
- {/* ═══════════════════════════════════════════════════════════════ */}
- <Suspense fallback={<BiddingsStatsCardsSkeleton />}>
- <BiddingsStatsCardsWrapper promises={promises} />
- </Suspense>
-
- {/* ═══════════════════════════════════════════════════════════════ */}
{/* 메인 테이블 */}
{/* ═══════════════════════════════════════════════════════════════ */}
<Suspense
@@ -92,44 +63,3 @@ export default async function BiddingsPage(props: IndexPageProps) {
</Shell>
)
}
-
-// ═══════════════════════════════════════════════════════════════
-// 통계 카드 래퍼 컴포넌트
-// ═══════════════════════════════════════════════════════════════
-async function BiddingsStatsCardsWrapper({
- promises
-}: {
- promises: Promise<[
- Awaited<ReturnType<typeof getBiddings>>,
- Awaited<ReturnType<typeof getBiddingStatusCounts>>,
- Awaited<ReturnType<typeof getBiddingTypeCounts>>,
- Awaited<ReturnType<typeof getBiddingManagerCounts>>,
- Awaited<ReturnType<typeof getBiddingMonthlyStats>>,
- ]>
-}) {
- const [biddingsResult, statusCounts, typeCounts, managerCounts, monthlyStats] = await promises
-
- return (
- <BiddingsStatsCards
- total={biddingsResult.total}
- statusCounts={statusCounts}
- typeCounts={typeCounts}
- managerCounts={managerCounts}
- monthlyStats={monthlyStats}
- />
- )
-}
-
-// 통계 카드 스켈레톤
-function BiddingsStatsCardsSkeleton() {
- return (
- <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
- {Array.from({ length: 4 }).map((_, i) => (
- <div key={i} className="rounded-lg border p-6">
- <div className="h-4 bg-muted rounded animate-pulse mb-2" />
- <div className="h-8 bg-muted rounded animate-pulse" />
- </div>
- ))}
- </div>
- )
-} \ No newline at end of file